import time
from binance.um_futures import UMFutures
from util.sys_const import *
from concurrent.futures import ThreadPoolExecutor, as_completed
from util.mysql_utils import *
from util.ExchangeCommon import Account
from util.tl_deal_result import tl_deal_result
from deal.close_postion import ClosePostion
import datetime
from util.UTILS import *


def get_decimal_places(number):
    # 将小数转换为字符串
    number_str = str(number)
    if 'e-0' in number_str:
        return int(number_str.split("e-0")[1])

    if number_str == '1.0':
        return 0;
    if '.' not in number_str:
        return 0;
    # 分离整数部分和小数部分
    integer_part, decimal_part = number_str.split('.')
    # 返回小数部分的长度
    return len(decimal_part)

# 配置日志
#config_logger(logger, logger.DEBUG)

# 初始化 U 本位合约客户端
client = UMFutures(key='P82r2pxV0g9SzjmvVMDjyTQMBjpdoFHTxlXarFLVODdfp12KXMPbHqToaVlxu7o5', secret='zt6IPo05G09Ml89EAsVR7P1LzQsnO5l9PRkxZO4Os389dBd9lfWh6NWw2ph9YQeM')

exchange = client.exchange_info()
preciseMap = {};
pricePreciseMap = {};
filterMap = {};
for symbolExchange in exchange['symbols']:
    contractType = symbolExchange['contractType']
    quantityPrecision = symbolExchange['quantityPrecision']
    pricePrecision = symbolExchange['pricePrecision'];
    if not contractType == 'PERPETUAL':
        continue
    symbol = symbolExchange['symbol']
    filterMap[symbol] = symbolExchange['filters'];
    stepSize = float(symbolExchange['filters'][1]['stepSize']);
    quantityPrecision = get_decimal_places(stepSize);
    preciseMap[symbol] = quantityPrecision
    tickSize = float(symbolExchange['filters'][0]['tickSize']);
    pricePrecision = get_decimal_places(tickSize);
    pricePreciseMap[symbol] = pricePrecision;
amount = 500  # 每个交易对的交易金额（USDT）
leverage = 10  # 杠杆倍数
max_positions = 5  # 最大持仓数量

# 用于同步的锁
#balance_lock = threading.Lock()
#positions_lock = threading.Lock()

# 全局变量来跟踪可用余额和持仓数量
available_balance = 0
open_position_count = 0

'''
mysql_utils = mysql_utils("rm-wz99662u81d4b7d6sso.mysql.rds.aliyuncs.com",
                          3306,
                          "suib_tjb",
                          "suib@2024",
                          "himalaya")
'''



mysql_utils = mysql_utils("localhost",
                          3306,
                          "root",
                          "joyingBox@123",
                          "himalaya")


monitorThreadPool = ThreadPoolExecutor(max_workers=5)

taskThreadPool = ThreadPoolExecutor(max_workers=6)

def get_all_funding_rates():
    try:
        funding_rate_list = client.mark_price()
        return funding_rate_list
    except Exception as e:
        logger.error(f"Error fetching funding rates: {e}")
        return {}


def place_market_order(symbol, side,quantity):
    try:
        params = {
            'symbol': symbol,
            'side': side,
            'type': 'MARKET',
            'quantity': quantity,  # 这里需要根据币安的精度要求进行调整
        }
        order = client.new_order(**params, recvWindow=30000)
        logger.info(f"Market order placed for {symbol}: {order}")
        return order
    except Exception as e:
        logger.error(f"Error placing market order for {symbol}: {e}")
        return None

def format_to_max_decimal(number):
    return '{:.1g}'.format(number)



def monitor(data):
    try:
        cp = ClosePostion(data)
        # print(11)
        logger.info(" monitory close postion ")
        cp.monitor(data)
    except Exception as e:
        logger.error(f"Error close position {e}")


def place_order(symbol,  side, quantity, fundingTime, rate):
    result = tl_deal_result();
    lastFundingRate = float(rate['lastFundingRate']);
    absLastRate = abs(lastFundingRate);
    if absLastRate > 0.005:
        result.strategy_id = 5;
    elif absLastRate > 0.002:
        result.strategy_id = 2;
    else:
        result.strategy_id = 1;

    try:
        stop_loss_percent = 0.08  # 2% 止损
        take_profit_percent = 0.006  # 3% 止盈

        params = {
            'symbol': symbol,
            'side': side,
            'type': 'MARKET',
            'quantity': quantity,  # 这里需要根据币安的精度要求进行调整
        }
        order = client.new_order(**params, recvWindow=30000)
        if order is None:
            # 下单失败
            logger.error("下单失败:" + symbol + ":" +str(fundingTime));
            return;

        orderId = order['orderId'];
        result.open_order_id = orderId
        result.position_side = side;
        result.symbol = symbol;

        result.funding_rate_time = fundingTime;


        current_time = datetime.now()
        current_time_seconds = int(current_time.timestamp());

        result.open_time = current_time_seconds
        result.funding_rate =  lastFundingRate
        logger.info(f"order: {symbol} {orderId} seconds until next funding time")
        time.sleep(0.5)
        for i in range(0,10):
            try:
                order_info = client.query_order(symbol=symbol, orderId=orderId, recvWindow=9000)
                break;
            except Exception as e:
                time.sleep(0.5)

        executed_qty = float(order_info['executedQty'])
        result.open_amount = executed_qty;
        avg_price = float(order_info['avgPrice'])
        result.open_price = avg_price;

        if order['side'] == 'BUY':
            stop_loss_price = avg_price * (1 - stop_loss_percent)
            take_profit_price = avg_price * (1 + take_profit_percent)
            close_side = 'SELL'
        else:
            stop_loss_price = avg_price * (1 + stop_loss_percent)
            take_profit_price = avg_price * (1 - take_profit_percent)
            close_side = 'BUY'

        pointNumber = get_decimal_places(avg_price)
        if pricePreciseMap.__contains__(symbol):
            pointNumber = pricePreciseMap[symbol];

        take_profit_price = float(round(take_profit_price, pointNumber))
        # 止盈
        logger.info(f" take_profit_price is {take_profit_price} {symbol}")
        profitOrder = client.new_order(
            symbol=symbol,
            side=close_side,
            price=take_profit_price,
            type="TAKE_PROFIT",
            quantity=executed_qty,
            stopPrice=take_profit_price,
            workingType="CONTRACT_PRICE",
            recvWindow=9000
        )

        if not profitOrder is None:
            result.stop_profit_order_id = profitOrder['orderId'];


        stop_loss_price = float(round(stop_loss_price, pointNumber))
        logger.info(f" stop_loss_price is {stop_loss_price} {symbol}")
        # 止损
        stopOrder = client.new_order(
            symbol=symbol,
            side=close_side,
            type="STOP",
            price=stop_loss_price,
            quantity=executed_qty,
            stopPrice=stop_loss_price,
            workingType="CONTRACT_PRICE"
            ,recvWindow=9000
        )


        if not stopOrder is None:
            result.stop_loss_order_id = stopOrder['orderId'];
        result.status = 0;

        mysql_utils.insert_deal_result(result)
        #executor.submit(process_symbol, rate['symbol'], rate)
        time.sleep(0.2)
        opening_data_list = mysql_utils.query("select * from tl_deal_result where open_order_id = " + str(orderId))
        if len(opening_data_list) > 0:
             logger.info(f" submit task {opening_data_list[0]}");
             monitorThreadPool.submit(monitor, opening_data_list[0])
        logger.info(
            f"For {symbol}: Stop loss set at {stop_loss_price} and take profit set at {take_profit_price} for {executed_qty} quantity")

    except Exception as e:
        errMsg:int = e.args[1];
        result.status = DEAL_RESULT_STATUS_EXCEPTION
        logger.exception(" error happen ")
        if errMsg == -2021:
            REAL_SIDE = 'BUY';
            if (side == 'SELL'):
                REAL_SIDE = 'BUY';
            else:
                REAL_SIDE = 'SELL';
            params = {
                'symbol': symbol,
                'side': REAL_SIDE,
                'type': 'MARKET',
                'quantity': quantity,  # 这里需要根据币安的精度要求进行调整
            }
            result.status = DEAL_RESULT_STATUS_FINISH
            placeOrder = client.new_order(**params, recvWindow=30000)
            result.stop_profit_order_id = placeOrder['orderId']
            result.reason = 10
            time.sleep(3)
            order_info = client.query_order(symbol=symbol, orderId=result.stop_profit_order_id, recvWindow=9000)
            avg_price = float(order_info['avgPrice'])
            result.close_price = avg_price;
            new_current_time = datetime.now()
            new_current_time_seconds = int(new_current_time.timestamp());
            result.close_time = new_current_time_seconds
            # 因为是做空 所以要反着来
            result.profit = (result.open_price  - result.close_price) * result.open_amount;



        mysql_utils.insert_deal_result(result)
        logger.error(f"Error setting stop loss and take profit for {symbol}: {e}")

def get_account(symbol):
    balances = client.balance(recvWindow=9000000);
    useBanlance = None;
    for balance in balances:
        if balance['asset'] == symbol:
            useBanlance = balance;
    return Account(Info=useBanlance, Balance=useBanlance['availableBalance'], FrozenBalance=None, Stocks=None,
                   FrozenStocks=None);


def get_available_positions():
    account_info = client.account(recvWindow=9000000);
    positions_info = account_info['positions']
    all_postions = [];
    for position in positions_info:
        amt = abs(float(position['positionAmt']))
        if amt > 0:
            all_postions.append(position)
    return all_postions;

#leftNumber = get_decimal_places(1.234);
#print(f"leftNumber is :{leftNumber} haha")
def process_symbol(symbol, rate):

    try:
        # 设置杠杆
        #client.change_leverage(symbol=symbol, leverage=leverage
        # 下市价单
        #order = place_market_order(symbol, 'SELL')
        logger.info(" runing for symbol:" + symbol);
        nextFundingTime = rate['nextFundingTime'];
        markPrice = float(rate['markPrice']);
        #rate['']
        singleMoney = rate['singleMoney'];
        positionSide = rate['positionSide'];
        quality_number = 1.0/markPrice;
        lenNumber = get_decimal_places(format_to_max_decimal(quality_number));
        if preciseMap.__contains__(symbol):
            lenNumber = preciseMap[symbol];
        if (lenNumber == 0):
            quantity = int(singleMoney / markPrice)
            place_order(symbol, positionSide, quantity, nextFundingTime, rate)
        else:
            quantity = round(singleMoney / markPrice, lenNumber)
            place_order(symbol, positionSide, quantity, nextFundingTime, rate)


    except Exception as e:
        logger.error(f"Error processing symbol {symbol}: {e}")
        logger.exception("An error occurred")




def process():
   # exchange:Exchange = SupportExchangeConfig.binance.value

    while True:
        try:
            current_time = datetime.now()
            real_time = int(current_time.timestamp() * 1000);
            time.sleep(2);
            funding_rates = get_all_funding_rates()
            # logger.info(f"Current funding rates: {funding_rates}")
            # update_account_info()
            minTime = 17227554970001;

            logger.info(f' real time is {real_time}')

            for rate in funding_rates:
                nextFundingTime = rate['nextFundingTime'];
                lastFundingRate = rate['lastFundingRate']
                rate_time = rate['time'];
                if nextFundingTime != 0 and rate_time > real_time:
                    minTime = min(minTime, rate['nextFundingTime'])

            logger.info(f' minTime is {minTime}')
            dt_object = datetime.fromtimestamp(minTime/1000)

            # 格式化为年月日
            formatted_date = dt_object.strftime('%Y-%m-%d %H:%M:%S')

            #print(formatted_date)
            logger.info(f"wait for funding time {formatted_date}");

            current_time = datetime.now()
            current_time_seconds = int(current_time.timestamp());
            # 减去120秒，留2分钟做最后的准备
            minTime_seconds = (minTime / 1000) - 2 * 60;
            waitTimeSeconds = minTime_seconds - current_time_seconds;
            #
            # next_hour = current_time.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
            # wait_time = (next_hour - current_time).total_seconds() + 1
            logger.info(f"Waiting for {waitTimeSeconds} seconds until next funding time")
            if waitTimeSeconds < 0:
                time.sleep(10);
                continue;
            # waitTimeSeconds = 10;
            time.sleep(waitTimeSeconds)

            funding_rates = get_all_funding_rates()

            # TODO 最大持仓笔数 需要配置
            max_position_number = 6;
            # TODO 每笔下单金额
            single_money = 200;

            # 获取当前的仓位
            logger.info(f"Current funding rates: {funding_rates}")
            positions = get_available_positions();
            # 余下的笔数
            left_number = max_position_number - len(positions);

            usedAmount = 0;
            for position in positions:
                positionAmount = abs(float(position['positionAmt'])) * float(position['entryPrice'])
                usedAmount = usedAmount + positionAmount;
            account: Account = get_account("USDT");
            # 10 倍杠杆
            balance = account.Balance * 10;
            leftAmount = balance - usedAmount;
            bufferAmount = 0.1 * leftAmount;

            if left_number <= 0:
                logger.info(" skip 仓位单量够了")
                continue

            if leftAmount <= bufferAmount:
                logger.info(" 金额不够")
                continue;

            trade_rates = [];

            filter_symbols = mysql_utils.query("select * from filter_symbol");
            dataMap = {};
            for filterSymbol in filter_symbols:
                dataMap[filterSymbol[2]] = filterSymbol;
            logger.info(f'filter data map is {dataMap}')
            
            for rate in funding_rates:
                nextFundingTime = rate['nextFundingTime'];
                lastFundingRate = float(rate['lastFundingRate'])
                symbol: str = rate['symbol']
                if symbol == 'ETHUSDT':
                    continue
                if symbol == 'BTCUSDT':
                    continue
                if left_number == 0:
                    continue
                if leftAmount <= bufferAmount:
                    continue;

                if dataMap.__contains__(symbol):
                    now_time = datetime.now();
                    record = dataMap[symbol];
                    startDate = record[3];
                    endDate = record[4];
                    if startDate < now_time and now_time < endDate:
                        logger.info(f"skip symbol {symbol}")
                        continue;


                if abs(lastFundingRate) > 0.005 and lastFundingRate < 0:
                    logger.info(f" add symbol {symbol} {nextFundingTime} {minTime}")
                    if nextFundingTime == minTime:
                        left_number = left_number - 1;
                        rate['singleMoney'] = single_money;
                        rate['positionSide'] = 'SELL';
                        # 减去下单金额
                        leftAmount = leftAmount - single_money;
                        trade_rates.append(rate);

            # 重新sleep
            logger.info(" trade rate size is : " + str(len(trade_rates)))
            current_time = datetime.now()
            current_time_seconds = int(current_time.timestamp());
            minTime_seconds = (minTime / 1000);
            waitTimeSeconds = minTime_seconds - current_time_seconds;
            logger.info(f"Waiting for {waitTimeSeconds} seconds until next funding time")
            # waitTimeSeconds = 10;
            if waitTimeSeconds > 0:
                time.sleep(waitTimeSeconds)

            # 保障资金费率已经结算
            time.sleep(1.1);


            futures = []
            for rate in trade_rates:
                futures.append(taskThreadPool.submit(process_symbol, rate['symbol'], rate))

            for future in as_completed(futures):
                future.result()
        except Exception as e:
            current_time = datetime.now()
            current_time_seconds = int(current_time.timestamp());
            logger.error(f"Error processing: timing {current_time_seconds}: {e}")
            time.sleep(1)


        # 更新账户信息


if __name__ == "__main__":
    getLogger('funding-rate')
    logger.info("start process")

    '''
    rate = {};
    symbol = 'TUSDT';
    rate['singleMoney'] = 10;
    rate['positionSide'] = 'SELL';
    rate['lastFundingRate'] = -0.00122045
    rate['nextFundingTime'] = 1722974400000
    rate['markPrice'] = 0.02399;
    process_symbol(symbol, rate);
    '''
    process()
    '''
    order = client.query_order("XEMUSDT", orderId=13471369763)
    print(order)
    '''

    '''
    funding_rate_list = get_all_funding_rates();
    for rate in funding_rate_list:
        symbol = rate['symbol'];
        lastFundingRate = float(rate['lastFundingRate'])
        if dataMap.__contains__(symbol):
            now_time = datetime.now();
            record = dataMap[symbol];
            startDate = record[3];
            endDate = record[4];
            if startDate < now_time and now_time < endDate:
                continue;
        if lastFundingRate < -0.001:
            #print(rate)
            rate['singleMoney'] = 10;
            rate['positionSide'] = 'SELL';
            #process_symbol(symbol, rate);
            break;
    '''
        #print(rate)
